﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using GameInterface;
using System.Xml;
using MainGame;

namespace GameLayer
{
    public class TiledLayer : Layer, ICollision<Sprite>
    {
        protected Sprite[] _sprites;
        protected int _tileWidth;
        protected int _tileHeight;
        protected int[,] _teles;
        protected int _rows;
        protected int _columns;

        protected Sprite[] Sprites
        {
            get { return _sprites; }
            set { _sprites = value; }
        }
        private List<Point> _disableAnimateCells;
        protected List<Point> DisableAnimateCells
        {
            get { return _disableAnimateCells; }
            set { _disableAnimateCells = value; }
        }
        protected int[,] Tiles
        {
            get { return _teles; }
            set { _teles = value; }
        }
        public int TileWidth
        {
            get { return _tileWidth; }
            set { _tileWidth = value; }
        }
        public int TileHeight
        {
            get { return _tileHeight; }
            set { _tileHeight = value; }
        }

        public TiledLayer(int rows, int columns, int tileWidth, int tileHeight, params Sprite[] sprites)
        {
            this.Sprites = sprites;
            this.TileWidth = tileWidth;
            this.TileHeight = tileHeight;
            this._rows = rows;
            this._columns = columns;
            this.Tiles = new int[rows, columns];
            DisableAnimateCells = new List<Point>();
        }
        public void SetSprite(int spriteIndex, Sprite value)
        {
            this.Sprites[spriteIndex] = value;
        }
        public Sprite GetSprite(int spriteIndex)
        {
            return this.Sprites[spriteIndex];
        }
        public void SetCell(int row, int column, int spriteIndex)
        {
            if (spriteIndex > this.Sprites.Length)
            {
                throw new IndexOutOfRangeException("spriteIndex invalid");
            }
            this.Tiles[row, column] = spriteIndex;
        }
        public Sprite GetCell(int row, int column)
        {
            if (row < 0 || column < 0 || row > this._rows || column > this._columns)
                return null;
            int index = this.Tiles[row, column];
            if (index < 0)
                return null;
            Sprite mySprite = (Sprite)Sprites[index].Clone();
            mySprite.Position = new Vector2(column * TileWidth, row * TileHeight) ;
            return mySprite;
        }
        public void EnableAnimateCell(Point cell, bool enable)
        {
            EnableAnimateCell(cell.X, cell.Y, enable);
        }
        public void EnableAnimateCell(int i, int j, bool enable)
        {
            if (!enable)
            {
                if (IsEnableAnimateCell(i, j))
                    DisableAnimateCells.Add(new Point(i, j));
            }
            else
            {
                DisableAnimateCells.Remove(new Point(i, j));
            }
        }
        public bool IsEnableAnimateCell(int i, int j)
        {
            foreach (Point p in DisableAnimateCells)
            {
                if (p.X == i && p.Y == j)
                {
                    return false;
                }
            }
            return true;
        }
        public override void Update(GameTime gameTime)
        {
            if (!Enable)
                return;
            foreach (Sprite s in this.Sprites)
            {
                s.Update(gameTime);
                s.NextFrame();
            }
        }

        public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, Viewport viewport)
        {
            if (!Visible)
                return;
            int rowStart = (int)Position.Y / TileHeight - 1;
            int colStart = (int)Position.X / TileWidth - 1;
            rowStart = (int)MathHelper.Clamp(rowStart - 1, 0, _rows - 1);
            colStart = (int)MathHelper.Clamp(colStart - 1, 0, _columns - 1);

            int rowEnd = (int)MathHelper.Clamp((Position.Y + viewport.Height) / TileHeight + 1, 0, _rows - 1);
            int colEnd = (int)MathHelper.Clamp((Position.X + viewport.Width) / TileWidth + 1, 0, _columns - 1);

            for (int i = rowStart; i < rowEnd; i++)
            {
                for (int j = colStart; j < colEnd; j++)
                {
                    if (this.Tiles[i, j] > -1)
                    {
                        this.Sprites[this.Tiles[i, j]].Draw(gameTime, spriteBatch, new Vector2(j * TileWidth, i * TileHeight) - Position);

                    }
//                    spriteBatch.DrawString(Round.font, String.Format("[{0},{1}]", i, j),new Vector2(j * TileWidth, i * TileHeight) - Position, Color.White);
                }
            }
        }

        public bool CollisionWith(Sprite sprite, bool pixelLevel)
        {
            if (!sprite.Visible || !this.Visible)
                return false;

            int rowStart = (int)sprite.Position.Y / TileHeight - 1;
            int colStart = (int)sprite.Position.X / TileWidth - 1;

            rowStart = (int)MathHelper.Clamp(rowStart , 0, _rows - 1);
            colStart = (int)MathHelper.Clamp(colStart , 0, _columns - 1);

            int rowEnd = (int)MathHelper.Clamp((sprite.Position.Y + sprite.Size.Y) / TileHeight + 1, 0, _rows - 1);
            int colEnd = (int)MathHelper.Clamp((sprite.Position.X + sprite.Size.X) / TileWidth + 1, 0, _columns - 1);

            for (int i = rowStart; i < rowEnd; i++)
                for (int j = colStart; j < colEnd; j++)
                {
                    Sprite s = this.GetCell(i, j);
                    if (s != null && s.CollisionWith(sprite, pixelLevel))
                        return true;
                }
            return false;
        }

        public static TiledLayer CreateTiledLayer(XmlElement xml, Sprite[] sprite)
        {
            TiledLayer tiledLayer = null;
            int rows = 0, columns = 0, tileWidth = 0, tileHeight = 0, x = 0, y = 0;
            rows = int.Parse(xml.GetAttribute("rows"));
            columns = int.Parse(xml.GetAttribute("columns"));
            int[,] tiles = new int[rows, columns];
            for (int i = 0; i < rows; i++)
                for (int j = 0; j < columns; j++)
                {
                    tiles[i, j] = -1;
                }

            tileWidth = int.Parse(xml.GetAttribute("tileWidth"));
            tileHeight = int.Parse(xml.GetAttribute("tileHeight"));

            if (!int.TryParse(xml.GetAttribute("x"), out x))
            {
                x = 0;
            }
            if (!int.TryParse(xml.GetAttribute("y"), out y))
            {
                y = 0;
            }
            XmlNodeList xmlTiles = xml.SelectNodes("Tiles/Tile");
            foreach (XmlElement xmlTile in xmlTiles)
            {
                int r = int.Parse(xmlTile.GetAttribute("row"));
                int c = int.Parse(xmlTile.GetAttribute("column"));
                int index = int.Parse(xmlTile.GetAttribute("index"));
                tiles[r, c] = index;
            }
            String type = xml.GetAttribute("type");
            if (String.IsNullOrEmpty(type))
            {
                tiledLayer = new TiledLayer(rows, columns, tileWidth, tileHeight, sprite);
            }
            else
            {
                tiledLayer = (TiledLayer)Activator.CreateInstance(Type.GetType(type), rows, columns, tileWidth, tileHeight, sprite);
            }
            tiledLayer.Tiles = tiles;
            tiledLayer.Position = new Vector2(x, y);

            return tiledLayer;
        }
    }
}
